#include "octreeVolume.h"
#include "slice2D.h"

//include these two will solve all the opengl compile and linking problem
#include <GL/glew.h>
#include <GL/glut.h>
#include <math.h>
#include <iostream>
#include <assert.h>
#include "mathFunc.h"
//helper functions
int exists(const char* filename)
{
	FILE *file;
	if(file = fopen(filename, "r"))
	{
		fclose(file);
		return 1;
	}
	return 0;
}


void updateTexSub3D(GLuint  tex, int offsetZ, int offsetY, int offsetX, int w, int h, int depth, GLenum type, void* data)
{
	glBindTexture(GL_TEXTURE_3D, tex);
	glTexSubImage3D(GL_TEXTURE_3D, 0, offsetX, offsetY, offsetZ, w, h, depth, GL_LUMINANCE, type, data);

}



//////////////////////////////////////////

octreeVolume::octreeVolume(volume3D *vol, int blockSize, int blockPoolDim)
{
	if(blockPoolDim==0)
	{
		int x,y,z;
		if(vol->getDimX()%blockSize==0)
			x=vol->getDimX()/blockSize;
		else
			x=(vol->getDimX()/blockSize +1);

		if(vol->getDimY()%blockSize==0)
			y=vol->getDimY()/blockSize;
		else
			y=(vol->getDimY()/blockSize +1);

		if(vol->getDimZ()%blockSize==0)
			z=vol->getDimZ()/blockSize;
		else
			z=(vol->getDimZ()/blockSize +1);

		int maxBlockDim=nearestPow(((x>y?x:y)>(x>z?x:z))?(x>y?x:y):(x>z?x:z));

		_blockSize = blockSize;

#ifdef _NO_BOUNDARY

		//TODO refactor following code to a seperate function
		_root = new octNode(0,0,0,blockSize);
		//TODO make safer
		_root->setLevel(log2(x));
		_root->setRange(0,0,0,vol->getDimZ()-1,vol->getDimY()-1,vol->getDimX()-1);

		_root->buildTree(_root->getLevel());
		_root->update();

		_root->buildDownSamplingTreeFromLeaf(vol);
		_root->update();

		_blockNum = _root->getBlockNumInSubTree();
		//std::cout<<"Total num of blocks: "<<_blockNum<<std::endl;
		//_root->getVolume()->writeToFile("beetle256X256X256_3.raw");

		_blockPoolDim = nearest3root(_root->getBlockNumInSubTree());
#else
		//TODO refactor following code to a seperate function
		_root = new octNode(0,0,0,_blockSize+1);
		//TODO make safer
		_root->setLevel(log2(maxBlockDim));
		vol->generateVirtualDim(_blockSize);
		_root->setRange(0,0,0,vol->getVirtualDimZ()-1,vol->getVirtualDimY()-1,vol->getVirtualDimX()-1);

		_root->buildTree(_root->getLevel());
		_root->update();
		//load data into tree and culling the empty node and volume
		_root->buildDownSamplingTreeWithBoundaryFromLeaf(vol, 0.1, 0.1);
		//_root->update();		
		//_root->cullVolumeByValue(0.06);
		_root->update();
		int index=0;
		_root->updateNodeIndex(_root,index);

		_blockNum = _root->getBlockNumInSubTree();
		_nodeNum = _root->getNodeNumInSubTree();
		std::cout<<"\nTotal num of blocks: "<<_blockNum<<std::endl;
		//_root->getVolume()->writeToFile("beetle256X256X256_3.raw");

		_blockPoolDim = nearest3root(_root->getBlockNumInSubTree());
		std::cout<<"_blockPoolDim: "<<_blockPoolDim<<std::endl;


		//test



#endif
		
	}
}

octreeVolume::~octreeVolume()
{
	if(_root)
		delete _root;
}



//void octreeVolume::generateBlockPoolGL()
//{
//}
//
//void octreeVolume::generateNodePoolGL()
//{
//}

//void octreeVolume::writeBlockPoolToFile(const char *filename)
//{
//	//TODO fix voxelByteSize;
//
//#ifdef _NO_BOUNDARY
//	volume3D *vol = new volume3D(_blockPoolDim*_blockSize,_blockPoolDim*_blockSize,_blockPoolDim*_blockSize, 1,0);
//#else
//	volume3D *vol = new volume3D(_blockPoolDim*(_blockSize+1),_blockPoolDim*(_blockSize+1),_blockPoolDim*(_blockSize+1), 1,0);
//#endif
//
//	int count=0;
//	generateBlockPool(vol, _root,count);
//	vol->writeToFile(filename);
//	free(vol);
//}

//void octreeVolume::writeNodePoolToFile(const char *filename)
//{
//	slice2D *slice = new slice2D(1,_nodeNum,3+8);
//	int count=0;
//	generateNodePool(slice, _root,count);
//	slice->writeToFile(filename);
//	slice->print();
//	free(slice);
//}

//void octreeVolume::write3DNodePoolToFile(const char* filename)
//{
//	nodePool3D* pool = new nodePool3D(_root);
//	int count=0;
//	//generate3DNodePool(pool, _root, count);
//
//	pool->writeToFile(filename);
//	free(pool);
//}

//tranverse
void octreeVolume::startTraverse() //call this function to start a new octree traversal
{
	_stack.empty();
	

}
int octreeVolume::next(octNode *& node) // return 0 when finish, 1 mean there is next
{
	return 1;

}

//recursive

//void octreeVolume::generateBlockPool(volume3D *vol, octNode* node, int &count)
//{
//	if(!node->isSingleColor())
//	{
//		int indexZ = count/(_blockPoolDim*_blockPoolDim);
//		int indexY = (count-indexZ*(_blockPoolDim*_blockPoolDim))/_blockPoolDim;
//		int indexX = count - indexZ*(_blockPoolDim*_blockPoolDim) - indexY*_blockPoolDim;
//#ifdef _NO_BOUNDARY
//		vol->addSubVolume(node->getVolume(),indexZ*_blockSize, indexY*_blockSize, indexX*_blockSize);
//#else
//		assert(node->getVolume());
//		vol->addSubVolume(node->getVolume(),indexZ*(_blockSize+1), indexY*(_blockSize+1), indexX*(_blockSize+1));
//#endif
//		//TODO (256/(_blockPoolDim-1)) may cause error in more level setup
//		//node->nodeInfo.blockPointer[0] = char(indexX)*(255/(_blockPoolDim-1));
//		//node->nodeInfo.blockPointer[1] = char(indexY)*(255/(_blockPoolDim-1));
//		//node->nodeInfo.blockPointer[2] = char(indexZ)*(255/(_blockPoolDim-1));
//		node->nodeInfo.blockPointer[0] = char(indexX);
//		node->nodeInfo.blockPointer[1] = char(indexY);
//		node->nodeInfo.blockPointer[2] = char(indexZ);
//		node->nodeInfo.selfIndex=count;
//		//std::cout<<"count:"<<count<<"  index: "<<indexZ<<" "<<indexY<<" "<<indexX<<std::endl;
//		count++;
//	}
//	else //if the node have single color
//	{
//		node->nodeInfo.singleValue = node->getVolumeSingleValue();
//		node->nodeInfo.selfIndex=count;
//		count++;
//	}
//		if(!node->isLeaf())
//		{
//			for(int k=0; k<2; k++)
//				for (int j=0; j<2; j++)
//					for(int i=0; i<2; i++)
//					{
//						generateBlockPool(vol, node->getChild(k,j,i), count);
//						
//					}
//
//		}
//
//}

//void octreeVolume::generateNodePool(slice2D *slice, octNode* node, int &count)
//{
//	for(int i=0; i<2; i++)
//		for (int j=0; j<2; j++)
//			for(int k=0; k<2; k++)
//		{
//			int offset = (k*4+j*2+i);
//			if(!node->isLeaf())
//					node->nodeInfo.childrenIndex=node->getChild(k,j,i)->nodeInfo.selfIndex;
//				else
//					node->nodeInfo.childrenIndex=255;
//			slice->setPixel(count,offset,node->nodeInfo.childrenIndex);
//		}
//	
//	slice->setPixel(count,8,node->nodeInfo.blockPointer[0]);
//	slice->setPixel(count,9,node->nodeInfo.blockPointer[1]);
//	slice->setPixel(count,10,node->nodeInfo.blockPointer[2]);
//
//	count++;
//
//	if(!node->isLeaf())
//	{
//		std::cout<<(int)node->getChild(0,0,0)->nodeInfo.selfIndex<<" "
//					<<(int)node->getChild(0,0,1)->nodeInfo.selfIndex<<" "
//					<<(int)node->getChild(0,1,0)->nodeInfo.selfIndex<<" "
//					<<(int)node->getChild(0,1,1)->nodeInfo.selfIndex<<" "
//					<<(int)node->getChild(1,0,0)->nodeInfo.selfIndex<<" "
//					<<(int)node->getChild(1,0,1)->nodeInfo.selfIndex<<" "
//					<<(int)node->getChild(1,1,0)->nodeInfo.selfIndex<<" "
//					<<(int)node->getChild(1,1,1)->nodeInfo.selfIndex<<" "
//					<<"------"<<std::endl;
//	}
//	if(!node->isLeaf())
//	{
//		for(int i=0; i<2; i++)
//			for (int j=0; j<2; j++)
//				for(int k=0; k<2; k++)
//				{
//					generateNodePool(slice, node->getChild(i,j,k), count);
//					
//				}
//
//	}
//
//}

void octreeVolume::generate3DNodePool(nodePool3D *pool, octNode* _root, int &count)
{

}



void octreeVolume::cullTreeByValue(float value)
{
	_root->cullVolumeByValue(value);
}

//get
octNode* octreeVolume::getRoot()
{
	return _root;
}
